package com.imay.videorecord.view.player;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Parcel;
import android.util.Log;
import android.view.Surface;

import com.imay.videorecord.encoder.clip.ClipTextureMovieEncoder;
import com.imay.videorecord.filter.base.MagicCameraInputFilter;
import com.imay.videorecord.filter.base.gpuimage.GPUImageFilter;
import com.imay.videorecord.filter.base.gpuimage.Rotation;
import com.imay.videorecord.filter.base.gpuimage.ScaleType;
import com.imay.videorecord.filter.base.gpuimage.TextureRotationUtil;
import com.imay.videorecord.utils.ImayCons;
import com.imay.videorecord.utils.ImayUtils;

import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;


/**
 * Created by Administrator on 2017/1/4.
 */
public class MyCustomRenderer implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
    private final String TAG = MyCustomRenderer.class.getSimpleName();

    public static final int NO_IMAGE = -1;

    public final Object mSurfaceChangedWaiter = new Object();
    private Context mContext;

    private int mTextureID = NO_IMAGE;
    private FloatBuffer mGLCubeBuffer;
    private FloatBuffer mGLTextureBuffer;

    private int mOutputWidth;
    private int mOutputHeight;
    private int mImageWidth;
    private int mImageHeight;

    private Rotation mRotation;
    private boolean mFlipHorizontal;
    private boolean mFlipVertical;
    private ScaleType mScaleType = ScaleType.CENTER_CROP;

    public SurfaceTexture mSurfaceTexture;
    public boolean updateSurface = false;
    private MediaPlayer mMediaPlayer;
    private int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
    private float[] mSTMatrix = new float[16];

    /**
     * 滤镜
     */
    private MagicCameraInputFilter cameraInputFilter; // 默认滤镜
    private GPUImageFilter mFilter; // 可变滤镜

    /**
     * 录制状态标志
     */
    private boolean recordingEnabled;
    private int recordingStatus;

    /**
     * 录制数据状态以及对应的Encoder
     */
    private static final int RECORDING_OFF = 0;
    private static final int RECORDING_ON = 1;
    private static final int RECORDING_RESUMED = 2;

    private ClipTextureMovieEncoder videoEncoder ;

    // 输出重塑文件
    private File outputFile;

    private OnFrameAvailableCallBack frameAvailableCallBack;
    private Parcel p;

    public MyCustomRenderer(Context context,MediaPlayer mediaPlayer) {
        mMediaPlayer = mediaPlayer;

        recordingEnabled = false;
        //mFilter = filter;
        mContext = context;

        initData(context);
    }

    public MyCustomRenderer(Context context,final GPUImageFilter filter, MediaPlayer mediaPlayer) {
        mMediaPlayer = mediaPlayer;
        recordingEnabled = false;
        mFilter = filter;

        mContext = context;

        initData(context);
    }

    private void initData(Context context) {
        // 录制标志
        recordingStatus = RECORDING_OFF;
        recordingEnabled = false;

        // 录制Encoder
        videoEncoder = new ClipTextureMovieEncoder(context);

        //
        outputFile = ImayUtils.getOutputMediaFile(context,ImayUtils.MEDIA_TYPE_VIDEO);

        // surface横纵数据流
        mGLCubeBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.CUBE.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mGLCubeBuffer.put(TextureRotationUtil.CUBE).position(0);

        mGLTextureBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.TEXTURE_NO_ROTATION.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        setRotation(Rotation.NORMAL, false, false);

        Matrix.setIdentityM(mSTMatrix, 0);
    }

    @Override
    public void onSurfaceCreated(final GL10 unused, final EGLConfig config) {
        Log.i(TAG,"onSurfaceCreated");
        int[] textures = new int[1];
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glGenTextures(1, textures, 0);
        mTextureID = textures[0];
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
        mSurfaceTexture = new SurfaceTexture(mTextureID);
        mSurfaceTexture.setOnFrameAvailableListener(this);

        Surface surface = new Surface(mSurfaceTexture);
        mMediaPlayer.setSurface(surface);
        surface.release();

        synchronized (this) {
            updateSurface = false;
        }

        /**
         * 初始化滤镜
         */
        if(cameraInputFilter == null)
            cameraInputFilter = new MagicCameraInputFilter(mContext);
        cameraInputFilter.init();
        if(mFilter != null){
            mFilter.init();
        }
    }

    @Override
    public void onSurfaceChanged(final GL10 gl, final int width, final int height) {
        Log.i(TAG,"onSurfaceChanged");
        mOutputWidth = width;
        mOutputHeight = height;
        GLES20.glViewport(0, 0, width, height);
        cameraInputFilter.onInputSizeChanged(mOutputWidth, mOutputHeight);
        if(mFilter == null){
            GLES20.glUseProgram(cameraInputFilter.getProgram());
            cameraInputFilter.onInputSizeChanged(width,height);
            cameraInputFilter.onOutputSizeChanged(width,height);
        }else{
            cameraInputFilter.initCameraFrameBuffer(mOutputWidth, mOutputHeight);
            GLES20.glUseProgram(mFilter.getProgram());
            mFilter.onOutputSizeChanged(width, height);
        }
        adjustSize(0, false, true);
        synchronized (mSurfaceChangedWaiter) {
            mSurfaceChangedWaiter.notifyAll();
        }
    }

    @Override
    public void onDrawFrame(final GL10 gl) {
        //Log.i(TAG,"onDrawFrame");
        if(mSurfaceTexture == null)
            return;
        mSurfaceTexture.updateTexImage();
        mSurfaceTexture.getTransformMatrix(mSTMatrix);
        // 重塑滤镜开始
        if (recordingEnabled) {
            switch (recordingStatus) {
                case RECORDING_OFF: // 开始录制
                    MediaMuxerRunnable.startMuxer(outputFile);
                    recordingStatus = RECORDING_ON;
                    break;
                case RECORDING_RESUMED:
                    //videoEncoder.updateSharedContext(EGL14.eglGetCurrentContext());
                    recordingStatus = RECORDING_ON;
                    /**
                     * 录制设置对应的纹理
                     */
                    if(recordingEnabled){
                        Log.i(TAG,"=====");
                        ByteBuffer ib = ByteBuffer.allocate(100 * 100);
                        GLES20.glReadPixels(0, 0, ImayCons.screenW, ImayCons.screenW, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib);
                        MediaMuxerRunnable.addVideoFrameData(ib.array());
                    }
                    break;
                case RECORDING_ON:
                    /**
                     * 录制设置对应的纹理
                     */
                    if(recordingEnabled){
                        Log.i(TAG,"=====");
                        ByteBuffer ib = ByteBuffer.allocate(100 * 100);
                        GLES20.glReadPixels(0, 0, ImayCons.screenW, ImayCons.screenW, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib);
                        MediaMuxerRunnable.addVideoFrameData(ib.array());
                    }
                    break;
                default:
                    throw new RuntimeException("unknown status " + recordingStatus);
            }
        } else {
            switch (recordingStatus) {
                case RECORDING_ON:
                case RECORDING_RESUMED:
                    MediaMuxerRunnable.stopMuxer();
                    recordingStatus = RECORDING_OFF;
                    break;
                case RECORDING_OFF:
                    break;
                default:
                    throw new RuntimeException("unknown status " + recordingStatus);
            }
        }



        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        float[] mtx = new float[16];
        mSurfaceTexture.getTransformMatrix(mtx);
        cameraInputFilter.setTextureTransformMatrix(mtx);
        int id = mTextureID;
        if(mFilter == null || mFilter instanceof MagicCameraInputFilter){
            cameraInputFilter.onDraw(mTextureID, mGLCubeBuffer, mGLTextureBuffer);
        }else{
            id = cameraInputFilter.onDrawToTexture(mTextureID);
            mFilter.onDraw(id, mGLCubeBuffer, mGLTextureBuffer);
        }

        /*Bitmap result = Bitmap.createBitmap(ImayParams.imageHeight, ImayParams.imageHeight, Bitmap.Config.ARGB_8888);
        result.copyPixelsFromBuffer(ib);*/
    }

    public void setFilter(final GPUImageFilter filter) {
        final GPUImageFilter oldFilter = mFilter;
        mFilter = filter;
        if (oldFilter != null) {
            oldFilter.destroy();
        }
        GLES20.glClearColor(0, 0, 0, 1);
        GLES20.glDisable(GLES20.GL_DEPTH_TEST);
        GLES20.glUseProgram(mFilter.getProgram());
        mFilter.onOutputSizeChanged(mOutputWidth, mOutputHeight);
        mFilter.init();

        if(cameraInputFilter == null){
            cameraInputFilter =new MagicCameraInputFilter(mContext);
            cameraInputFilter.init();
        }
        cameraInputFilter.initCameraFrameBuffer(mOutputWidth, mOutputHeight);
    }


    public void setScaleType(ScaleType scaleType) {
        mScaleType = scaleType;
    }

    protected int getFrameWidth() {
        return mOutputWidth;
    }

    protected int getFrameHeight() {
        return mOutputHeight;
    }

    private void adjustImageScaling() {
        float outputWidth = mOutputWidth;
        float outputHeight = mOutputHeight;

        if (mRotation == Rotation.ROTATION_270 || mRotation == Rotation.ROTATION_90) {
            outputWidth = mOutputHeight;
            outputHeight = mOutputWidth;
        }

        mImageWidth = ImayCons.screenW;
        mImageHeight = ImayCons.screenW;

        outputWidth = ImayCons.screenW;
        outputHeight = ImayCons.screenW;

        float ratio1 = outputWidth / mImageWidth;
        float ratio2 = outputHeight / mImageHeight;
        float ratioMax = Math.max(ratio1, ratio2);
        int imageWidthNew = Math.round(mImageWidth * ratioMax);
        int imageHeightNew = Math.round(mImageHeight * ratioMax);

        float ratioWidth = imageWidthNew / outputWidth;
        float ratioHeight = imageHeightNew / outputHeight;

        float[] cube = TextureRotationUtil.CUBE;
        float[] textureCords = TextureRotationUtil.getRotation(mRotation, mFlipHorizontal, mFlipVertical);
        if (mScaleType == ScaleType.CENTER_CROP) {
            float distHorizontal = (1 - 1 / ratioWidth) / 2;
            float distVertical = (1 - 1 / ratioHeight) / 2;
            textureCords = new float[]{
                    addDistance(textureCords[0], distHorizontal), addDistance(textureCords[1], distVertical),
                    addDistance(textureCords[2], distHorizontal), addDistance(textureCords[3], distVertical),
                    addDistance(textureCords[4], distHorizontal), addDistance(textureCords[5], distVertical),
                    addDistance(textureCords[6], distHorizontal), addDistance(textureCords[7], distVertical),
            };
        } else {
            cube = new float[]{
                    TextureRotationUtil.CUBE[0] / ratioHeight, TextureRotationUtil.CUBE[1] / ratioWidth,
                    TextureRotationUtil.CUBE[2] / ratioHeight, TextureRotationUtil.CUBE[3] / ratioWidth,
                    TextureRotationUtil.CUBE[4] / ratioHeight, TextureRotationUtil.CUBE[5] / ratioWidth,
                    TextureRotationUtil.CUBE[6] / ratioHeight, TextureRotationUtil.CUBE[7] / ratioWidth,
            };
        }

        mGLCubeBuffer.clear();
        mGLCubeBuffer.put(cube).position(0);
        mGLTextureBuffer.clear();
        mGLTextureBuffer.put(textureCords).position(0);
    }

    private void adjustSize(int rotation, boolean flipHorizontal, boolean flipVertical){
        float outputWidth = mOutputWidth;
        float outputHeight = mOutputHeight;
        float[] textureCords = com.imay.videorecord.utils.TextureRotationUtil.getRotation(Rotation.fromInt(rotation),
                flipHorizontal, flipVertical);
        float[] cube = com.imay.videorecord.utils.TextureRotationUtil.CUBE;

        mImageWidth = ImayCons.screenW;
        mImageHeight = ImayCons.screenW;

        float ratio1 = outputWidth / mImageWidth;
        float ratio2 = outputHeight / mImageHeight;
        float ratioMax = Math.max(ratio1, ratio2);
        int imageWidthNew = Math.round(mImageWidth * ratioMax);
        int imageHeightNew = Math.round(mImageHeight * ratioMax);

        float ratioWidth = imageWidthNew / outputWidth;
        float ratioHeight = imageHeightNew / outputHeight;

        if(mScaleType == mScaleType.CENTER_INSIDE){
            cube = new float[]{
                    com.imay.videorecord.utils.TextureRotationUtil.CUBE[0] / ratioHeight, com.imay.videorecord.utils.TextureRotationUtil.CUBE[1] / ratioWidth,
                    com.imay.videorecord.utils.TextureRotationUtil.CUBE[2] / ratioHeight, com.imay.videorecord.utils.TextureRotationUtil.CUBE[3] / ratioWidth,
                    com.imay.videorecord.utils.TextureRotationUtil.CUBE[4] / ratioHeight, com.imay.videorecord.utils.TextureRotationUtil.CUBE[5] / ratioWidth,
                    com.imay.videorecord.utils.TextureRotationUtil.CUBE[6] / ratioHeight, com.imay.videorecord.utils.TextureRotationUtil.CUBE[7] / ratioWidth,
            };
        }else if(mScaleType == mScaleType.CENTER_CROP){
            float distHorizontal = (1 - 1 / ratioWidth) / 2;
            float distVertical = (1 - 1 / ratioHeight) / 2;
            textureCords = new float[]{
                    addDistance(textureCords[0], distVertical), addDistance(textureCords[1], distHorizontal),
                    addDistance(textureCords[2], distVertical), addDistance(textureCords[3], distHorizontal),
                    addDistance(textureCords[4], distVertical), addDistance(textureCords[5], distHorizontal),
                    addDistance(textureCords[6], distVertical), addDistance(textureCords[7], distHorizontal),
            };
        }
        mGLCubeBuffer.clear();
        mGLCubeBuffer.put(cube).position(0);
        mGLTextureBuffer.clear();
        mGLTextureBuffer.put(textureCords).position(0);
    }

    private float addDistance(float coordinate, float distance) {
        return coordinate == 0.0f ? distance : 1 - distance;
    }

    public void setRotationCamera(final Rotation rotation, final boolean flipHorizontal,
                                  final boolean flipVertical) {
        setRotation(rotation, flipVertical, flipHorizontal);
    }

    public void setRotation(final Rotation rotation) {
        mRotation = rotation;
        adjustImageScaling();
    }

    public void setRotation(final Rotation rotation,
                            final boolean flipHorizontal, final boolean flipVertical) {
        mFlipHorizontal = flipHorizontal;
        mFlipVertical = flipVertical;
        setRotation(rotation);
    }

    public Rotation getRotation() {
        return mRotation;
    }

    public boolean isFlippedHorizontally() {
        return mFlipHorizontal;
    }

    public boolean isFlippedVertically() {
        return mFlipVertical;
    }


    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        //updateSurface = true;
        // 回调到GLSurfaceView
        if(frameAvailableCallBack != null){
            frameAvailableCallBack.callBack();
        }
    }

    /**
     * 释放资源数据
     */
    public void release(){
        mSurfaceTexture.release();
        cameraInputFilter.destroy();
        mGLCubeBuffer.clear();
        mGLTextureBuffer.clear();
        mSurfaceTexture = null;
        cameraInputFilter = null;

        if(mFilter != null){
            mFilter.destroy();
            mFilter = null;
        }
    }

    /**
     * 重塑滤镜
     * @param isRecording
     */
    public void changeRecordingState(boolean isRecording) {
        recordingEnabled = isRecording;
    }

    public void setFrameAvailableCallBack(OnFrameAvailableCallBack frameAvailableCallBack) {
        this.frameAvailableCallBack = frameAvailableCallBack;
    }
}